#include "TcpServer.h"
#include "TcpConnection.h"


TcpServer::TcpServer(EventLoop *loop, const InetAddress &listenAddr)
: _loop(loop)
, _name("TcpServer")
, _acceptor(std::make_unique<Acceptor>(loop, listenAddr))
, _connectionCallback()
, _messageCallback()
, _started(false)
, _nextConnId(1)
{
    _acceptor->setNewConnectionCallback(
        std::bind(&TcpServer::newConnection, this, std::placeholders::_1, std::placeholders::_2));
    _acceptor->listen();
    cout << "TcpServer listening on " << listenAddr.ip() << ":" << listenAddr.port() << "\n";
}
TcpServer::~TcpServer()
{

}

void TcpServer::newConnection(int sockfd, const InetAddress &peerAddr)
{
    _loop->assertInLoopThread();
    char buf[64];
    snprintf(buf, sizeof(buf), ":%s#%d", _name.c_str(), _nextConnId);
    ++_nextConnId;
    string connName = _name + buf;

    std::cerr << "TcpServer new connection from " << peerAddr.ip() << ":" << peerAddr.port()
              << " to " << connName << "\n";
    InetAddress localAddr(getLocalAddr(sockfd));
//set callback
    auto conn = std::make_shared<TcpConnection>(_loop, connName, sockfd, localAddr, peerAddr);
    conn->setConnectCallback(_connectionCallback);
    conn->setMessageCallback(_messageCallback);
    _connections[connName] = conn;
    conn->setCloseCallback(
        std::bind(&TcpServer::removeConnection, this, std::placeholders::_1));
    conn->connectEstablished();
}
void TcpServer::removeConnection(const TcpConnectionPtr &conn)
{
    _loop->assertInLoopThread();
    std::cerr << "TcpServer remove connection " << conn->name() << "\n";
    assert(_connections.find(conn->name()) != _connections.end());
    
    size_t n = _connections.erase(conn->name());
    assert(n == 1);
    _loop->queueInLoop(std::bind(&TcpConnection::connectDestroyed, conn));
}
struct sockaddr_in TcpServer::getLocalAddr(int sockfd)
{
    struct sockaddr_in addr;
    socklen_t addrLen = sizeof(addr);
    if (::getsockname(sockfd, (struct sockaddr *)&addr, &addrLen) < 0)
    {
        perror("TcpServer::getLocalAddr getsockname");
        std::exit(1);
    }
    return addr;
}
struct sockaddr_in TcpServer::getPeerAddr(int sockfd)
{
    struct sockaddr_in addr;
    socklen_t addrLen = sizeof(addr);
    if (::getpeername(sockfd, (struct sockaddr *)&addr, &addrLen) < 0)
    {
        perror("TcpServer::getPeerAddr getpeername");
        std::exit(1);
    }
    return addr;
}